package com.zks.handler;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.log.Log;
import cn.hutool.log.StaticLog;
import com.zks.Listener.FileProgressiveFutureListener;
import com.zks.setting.Setting;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.DefaultFileRegion;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Set;

public class Response {
    private static final Log log = StaticLog.get();

    private HttpVersion httpVersion = HttpVersion.HTTP_1_1;
    /** 返回内容类型：普通文本 */
    public final static String CONTENT_TYPE_TEXT = "text/plain";
    /** 返回内容类型：HTML */
    public final static String CONTENT_TYPE_HTML = "text/html";
    /** 返回内容类型：XML */
    public final static String CONTENT_TYPE_XML = "text/xml";
    private ChannelHandlerContext ctx;
    private Request request;
    //响应内容
    private Object content = Unpooled.EMPTY_BUFFER;
    //响应状态
    private HttpResponseStatus status = HttpResponseStatus.OK;
    //响应头
    private HttpHeaders headers = new DefaultHttpHeaders();
    private Set<io.netty.handler.codec.http.cookie.Cookie> cookies = new HashSet<Cookie>();

    private String charset = Setting.getCharset();
    private String contentType = CONTENT_TYPE_HTML;


    //发送完成标记
    private boolean isSent;
    public Response(ChannelHandlerContext ctx, Request request) {
        this.ctx = ctx;
        this.request = request;
    }

    /**
     * 构建response 对象
     *
     * @return com.zks.handler.Response
     * @author zks
     * @date 2019/4/14 12:40
     */
    protected static Response build(ChannelHandlerContext ctx, Request request) {
        return new Response(ctx, request);
    }



    /**
     * @return 是否已经出发发送请求，内部使用<br>
     */
    protected boolean isSent(){
        return this.isSent;
    }
    /**
     * 设置响应内容
     *
     * @return com.zks.handler.Response
     * @author zks
     * @date 2019/4/14 12:52
     */
    public Response setContext(String content) {
        this.content = Unpooled.copiedBuffer(content, Charset.forName(charset));
        return this;
    }

    public ChannelFuture sendError(HttpResponseStatus status, String msg) {
        if (ctx.channel().isActive()) {
            return this.setStatus(status).setContext(msg).send();

        }
        return null;
    }

    public Response setStatus(HttpResponseStatus status) {
        this.status = status;
        return this;
    }


    /**
     * 发送数据到客户端
     *
     * @return io.netty.channel.ChannelFuture
     * @author zks
     * @date 2019/4/14 13:28
     */
    public ChannelFuture send() {
        ChannelFuture channelFuture;
        if (content instanceof File) {
            //文件
            File file = (File)content;
            try {
                channelFuture = sendFile(file);
            } catch (IOException e) {
                log.error(StrUtil.format("Send {} error!", file), e);
                channelFuture = sendError(HttpResponseStatus.FORBIDDEN, "");
            }
        } else {

            channelFuture = sendFull();

        }
        this.isSent = true;
        return channelFuture;
    }

    private ChannelFuture sendFile(File file) throws IOException {
        final RandomAccessFile raf = new RandomAccessFile(file, "r");

        // 内容长度
        long fileLength = raf.length();
        this.setContentLength(fileLength);

        //文件类型
        String contentType = HttpUtil.getMimeType(file.getName());
        if(StrUtil.isBlank(contentType)){
            //无法识别默认使用数据流
            contentType = "application/octet-stream";
        }
        this.setContentType(contentType);

        ctx.write(this.toDefaultHttpResponse());
        ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise())
                .addListener(FileProgressiveFutureListener.build(raf));

        return sendEmptyLast();
    }



    /**
     * 发送结尾标记，表示发送结束
     * @return ChannelFuture
     */
    private ChannelFuture sendEmptyLast(){
        final ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
        if (false == request.isKeepAlive()) {
            lastContentFuture.addListener(ChannelFutureListener.CLOSE);
        }

        return lastContentFuture;
    }

    public Response setContentType(String contentType) {
        this.contentType = contentType;
        return this;
    }

    /**
     * 设置响应体长度
     *
     * @param contentLength 响应体长度
     * @return 自己
     */
    public Response setContentLength(long contentLength) {
        setHeader(HttpHeaderNames.CONTENT_LENGTH.toString(), contentLength);
        return this;
    }

    private DefaultHttpResponse toDefaultHttpResponse() {
        final DefaultHttpResponse defaultHttpResponse = new DefaultHttpResponse(httpVersion, status);

        // headers
        final HttpHeaders httpHeaders = defaultHttpResponse.headers().add(headers);

        // Cookies
        for (Cookie cookie : cookies) {
            httpHeaders.add(HttpHeaderNames.SET_COOKIE.toString(), ServerCookieEncoder.LAX.encode(cookie));
        }

        return defaultHttpResponse;
    }
    private ChannelFuture sendFull() {
        if (request != null && request.isKeepAlive()) {
            setKeepAlive();
            return ctx.writeAndFlush(this.toFullHttpResponse());
        }else{

            return sendAndCloseFull();

        }

    }
    /**
     * 发送给到客户端并关闭ChannelHandlerContext
     *
     * @return ChannelFuture
     */
    private ChannelFuture sendAndCloseFull() {
        return ctx.writeAndFlush(this.toFullHttpResponse()).addListener(ChannelFutureListener.CLOSE);
    }

    public Response setKeepAlive() {
        setHeader(HttpHeaderNames.CONNECTION.toString(), HttpHeaderValues.KEEP_ALIVE.toString());
        return this;
    }

    public Response setHeader(String name, Object value) {
        headers.set(name, value);
        return this;
    }


    private FullHttpResponse toFullHttpResponse() {
        final ByteBuf byteBuf=(ByteBuf)content;
        final FullHttpResponse fullHttpResponse=new DefaultFullHttpResponse(httpVersion,status,byteBuf);
        // headers
        final HttpHeaders httpHeaders = fullHttpResponse.headers().add(headers);
        httpHeaders.set(HttpHeaderNames.CONTENT_TYPE.toString(), StrUtil.format("{};charset={}", contentType, charset));
        httpHeaders.set(HttpHeaderNames.CONTENT_ENCODING.toString(), charset);
        httpHeaders.set(HttpHeaderNames.CONTENT_LENGTH.toString(), byteBuf.readableBytes());
        //cookie
        httpHeaders.set(HttpHeaderNames.COOKIE.toString(), ServerCookieEncoder.LAX.encode(cookies));
        return fullHttpResponse;
    }


    public ChannelFuture sendServerError(String msg) {
        return sendError(HttpResponseStatus.INTERNAL_SERVER_ERROR, msg);
    }


    public Response setContent(String contentText) {
        this.content = Unpooled.copiedBuffer(contentText, Charset.forName(charset));
        return this;
    }
}
